﻿using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Threading;

using System.Net;
using System.Net.Sockets;
//using Urd.Common;

namespace Urd.Packet
{
    public delegate void awaiter_p(connect_parralel p);

    public sealed class connect_parralel:IDisposable
    {
        //private static ManualResetEvent connectDone =
        //        new ManualResetEvent(false);
        private ManualResetEvent writeDone =
            new ManualResetEvent(false);
        private ManualResetEvent readDone =
            new ManualResetEvent(false);
        //private static Random r = new Random(1234567);
        string lasterror;
        
        //indicates sequience ready to run
        bool ready;
        public bool Ready { get { return ready; } }
        //indicates sequience recieved something;
        bool recieved;
        public bool Recieved { get { return recieved; } }

        private sendinfo S;
        private Queue<TimeSpan> lagzz;
        public Queue<TimeSpan> lag { get { return lagzz; } }

        struct connectinfo
        {
            public TcpClient client;
            public sendinfo item;
            public DateTime start;
            public NetworkStream stream;
            public byte[] data;

            public connectinfo(ref TcpClient c, sendinfo item, NetworkStream ns, byte[] buffer)
            {
                client = c;
                start = DateTime.Now;
                stream = ns;
                data = buffer;
                this.item = item;
            }
        }
        public void Dispose() 
        {
            readDone.Dispose();
            writeDone.Dispose();
            
        }
        Thread writethread;
        Thread readthread;
        IPAddress ip;
        int port;
        awaiter_p a;
        string name;
        public connect_parralel(IPAddress ip, int port, string name = "")
        {
            lagzz = new Queue<TimeSpan>();
            S = null;
            ready = true;
            recieved = false;
            this.ip = ip;
            this.port = port;
            this.name = name;
        }
        public void Connect(sendinfo data, awaiter_p done_callback = null)
        {
            if (ready)
            {
                ready = false;
                this.a = done_callback;
                TcpClient client = new TcpClient(); 
                client.ReceiveTimeout = 1;
                client.SendTimeout = 1;
                client.BeginConnect(
                    ip,
                    port,
                    connect_callback,
                    new connectinfo(ref client, data, null, null));
            }
        }


        private void connect_callback(IAsyncResult res)
        {
            connectinfo cinfo = (connectinfo)res.AsyncState;


            recieved = false;
            try
            {
                readDone.Reset();
                writeDone.Reset();
                
                cinfo.client.EndConnect(res);
                cinfo.stream = cinfo.client.GetStream();

                cinfo.data = new byte[8096];
                //  readDone.Reset();
                //сюда пишем координаты для говтого или нового объекта
                cinfo.stream.BeginWrite(cinfo.item.tosend.Bytes, 0, cinfo.item.tosend.Bytes.Length, write_callback, cinfo);
                //и отсюда сразуже читаем дамп базы.
                cinfo.stream.BeginRead(cinfo.data, 0, cinfo.data.Length, read_callback, cinfo);

                readDone.WaitOne();
                writeDone.WaitOne();


            }
            catch(Exception e)
            {
                lasterror = e.Message;
                // ready = true;
               // if (a != null) a(this);
            }
            finally 
            {
                recieved = true;
                ready = true;
                if (a != null) a(this);
                if(cinfo.stream!=null) cinfo.stream.Close();
                if(cinfo.client!=null) cinfo.client.Close();
            }
        }
        private void write_callback(IAsyncResult res)
        {
            connectinfo cinfo = (connectinfo)res.AsyncState;
            cinfo.stream.EndWrite(res);
            writeDone.Set();
        }

        private void read_callback(IAsyncResult res)
        {
            connectinfo cinfo = (connectinfo)res.AsyncState;
            try
            {
                cinfo.stream.EndRead(res);
                byte[] data = cinfo.data;
                packet ret = new packet(cinfo.data);

                sendinfo info = new sendinfo(ret, (DateTime.Now - cinfo.start), cinfo.item.tosend);
                //if(info.lag.Milliseconds>10)
                lagzz.Enqueue(info.lag);
                if (lagzz.Count > 100) lagzz.Dequeue();

                S = info;
            }
            catch (Exception e) 
            {
                lasterror = e.Message;
                S = new sendinfo(new packet(Opcode.Error, Command.Get), (DateTime.Now - cinfo.start), cinfo.item.tosend);
            }
            finally
            {
                readDone.Set();
                
            }
        }

      




        public sendinfo s { get { return S; } }
    }
}

